mod utils;

use hidapi::{HidDevice};
use jni::objects::*;
use jni::sys::*;
use jni::JNIEnv;
use utils::hidapi::API;
use utils::log::*;




#[no_mangle]
pub unsafe extern "C" fn Java_me_yricky_usbhid_UsbHid_nGetDevices(env: JNIEnv, _class: JClass) ->jobject {
    log!("Call success!");
    let j_list = env
        .new_object("java/util/ArrayList","()V",&[])
        .expect("Error during construct object");
    log!("Create list Success!");
    let res = API.refresh_devices();
    for device in API.device_list() {

        let j_dev_info = env.new_object(
            "me/yricky/usbhid/HidDevice",
            "(Ljava/lang/String;SSLjava/lang/String;SLjava/lang/String;Ljava/lang/String;IB)V",
            &[
                JValue::Object(JObject::from(env.new_string(device.path().to_str().unwrap()).unwrap())),
                JValue::Short(device.vendor_id() as jshort) ,
                JValue::Short(device.product_id() as jshort),
                JValue::Object(JObject::from(env.new_string(device.serial_number().unwrap_or_default()).unwrap())),
                JValue::Short(device.release_number() as jshort),
                JValue::Object(JObject::from(env.new_string(device.manufacturer_string().unwrap_or_default()).unwrap())),
                JValue::Object(JObject::from(env.new_string(device.product_string().unwrap_or_default()).unwrap())),
                JValue::Int(device.interface_number() as jint),
                JValue::Byte(device.bus_type() as jbyte)
            ]
        ).unwrap();
        env.call_method(
            j_list,
            "add",
            "(Ljava/lang/Object;)Z",
            &[JValue::Object(j_dev_info.into())]
        ).expect("Error during add devInfo to list!");
    }
    j_list.into_raw()
}

#[no_mangle]
pub unsafe extern "C" fn Java_me_yricky_usbhid_HidDevice_nOpenDevice(
    env: JNIEnv, _class: JClass,
    path:JString
) ->jlong {
    let dev = API.open_path(env.get_string(path).unwrap().as_ref());
    match dev {
        Ok(dev) => {
            let b = Box::new(dev);
            return Box::into_raw(b) as jlong
        }
        Err(e) =>{
            log!("{:#?}",e);
            0
        }
    }
}

#[no_mangle]
pub unsafe extern "C" fn Java_me_yricky_usbhid_HidDevice_nClose(
    env: JNIEnv, _class: JClass,
    ptr:jlong,
) {
    let dev = Box::from_raw(ptr as *mut HidDevice);
    log!("Close:{:#?}",dev.get_device_info().unwrap().path());
    drop(dev)
}

#[no_mangle]
pub unsafe extern "C" fn Java_me_yricky_usbhid_HidDevice_nReadBytes(
    env: JNIEnv, _class: JClass,
    ptr:jlong,
    ba:jbyteArray,
    time_out:jint
) {
    let size = env.get_array_length(ba).unwrap() as usize;
    let mut bytes:Vec<u8> = Vec::with_capacity(size);
    bytes.set_len(size);
    log!("read size:{:#?}",bytes.as_mut_slice().len());
    let dev = Box::from_raw(ptr as *mut HidDevice);
    log!("{:#?}",dev.get_device_info().unwrap().path());
    let err = dev.read_timeout(bytes.as_mut_slice(), time_out);
    match err {
        Ok(_size) =>{}
        Err(e) =>{
            log!("Err:{:#?}",e)
        }
    }
    log!("read:{:#?}", bytes);
    let u8slice = bytes.as_slice();
    let jb_arr:&[jbyte] = std::slice::from_raw_parts(u8slice.as_ptr() as *const jbyte, u8slice.len());
    env.set_byte_array_region(ba, 0, jb_arr).expect("Copy byteArray error");
    Box::into_raw(dev);
}

#[no_mangle]
pub unsafe extern "C" fn Java_me_yricky_usbhid_HidDevice_nWriteBytes(
    env: JNIEnv, _class: JClass,
    ptr:jlong,
    ba:jbyteArray,
) {
    let size = env.get_array_length(ba).unwrap() as usize;
    let mut bytes:Vec<jbyte> = Vec::with_capacity(size);
    bytes.set_len(size);
    let dev = Box::from_raw(ptr as *mut HidDevice);
    env.get_byte_array_region(ba,0,bytes.as_mut_slice()).unwrap();
    log!("{:#?}",dev.get_device_info().unwrap().path());
    let u8slice = bytes.as_slice();
    let arr:&[u8] = std::slice::from_raw_parts(u8slice.as_ptr() as *const u8, u8slice.len());
    let err = dev.write(arr);
    log!("write:{:#?}",arr);
    match err {
        Ok(_size) =>{}
        Err(e) =>{
            log!("Err:{:#?}",e)
        }
    }
    Box::into_raw(dev);
}

#[no_mangle]
pub unsafe extern "C" fn Java_me_yricky_usbhid_UsbHid_nEnableLog(env: JNIEnv, _class: JClass,enable:jboolean) {
    ENABLE_LOG = enable != 0
}